/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.acquire.explorer.gui.central.meta.panel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import org.dcm4che3.data.Tag;
import org.weasis.acquire.explorer.AcquireImageInfo;
import org.weasis.acquire.explorer.gui.central.meta.model.AcquireMetadataTableModel;
import org.weasis.core.api.gui.util.JMVUtils;
import org.weasis.core.api.media.data.TagW;
import org.weasis.core.api.media.data.TagW.TagType;
import org.weasis.core.api.service.BundleTools;
import org.weasis.core.api.util.FontTools;
import org.weasis.core.api.util.LocalUtil;
import org.weasis.core.api.util.StringUtil;
import org.weasis.dicom.codec.TagD;
import org.weasis.dicom.codec.display.Modality;
import com.github.lgooddatepicker.components.DatePickerSettings;
import com.github.lgooddatepicker.components.DatePickerSettings.DateArea;
import com.github.lgooddatepicker.tableeditors.DateTableEditor;
import com.github.lgooddatepicker.tableeditors.TimeTableEditor;
@SuppressWarnings("serial")
public abstract class AcquireMetadataPanel extends JPanel implements TableModelListener {
protected final String title;
protected final JLabel label = new JLabel();
protected final JTable table;
protected final JScrollPane tableScroll;
protected AcquireImageInfo imageInfo;
protected TitledBorder titleBorder;
public AcquireMetadataPanel(String title) {
setLayout(new BorderLayout());
this.title = title;
this.titleBorder =
new TitledBorder(null, getDisplayText(), TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION);
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(7, 3, 0, 3), titleBorder));
tableScroll = new JScrollPane();
tableScroll.setBorder(BorderFactory.createEmptyBorder(7, 3, 0, 3));
table = new JTable();
// Force to commit value when losing the focus
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); //$NON-NLS-1$
table.setFont(FontTools.getFont11()); // Default size
table.getTableHeader().setReorderingAllowed(false);
updateTable();
setMetaVisible(false);
add(tableScroll, BorderLayout.CENTER);
}
public abstract AcquireMetadataTableModel newTableModel();
public String getDisplayText() {
return title;
}
public void stopEditing() {
TableCellEditor editor = table.getCellEditor();
if (editor != null) {
editor.stopCellEditing();
}
}
@Override
public Font getFont() {
return FontTools.getFont12Bold();
}
public void setImageInfo(AcquireImageInfo imageInfo) {
this.imageInfo = imageInfo;
this.titleBorder.setTitle(getDisplayText());
setMetaVisible(imageInfo != null);
update();
}
public void setMetaVisible(boolean visible) {
tableScroll.setVisible(visible);
}
public void update() {
updateLabel();
updateTable();
}
public void updateLabel() {
this.titleBorder.setTitle(getDisplayText());
}
public void updateTable() {
AcquireMetadataTableModel model = newTableModel();
model.addTableModelListener(this);
table.setModel(model);
table.getColumnModel().getColumn(1).setCellRenderer(new TagRenderer());
table.getColumnModel().getColumn(1).setCellEditor(new AcquireImageCellEditor());
int height = Optional.ofNullable(table.getFont()).map(f -> f.getSize() + 10).orElse(24);
for (int i = 0; i < table.getRowCount(); i++) {
table.setRowHeight(i, height);
}
JPanel tableContainer = new JPanel(new BorderLayout());
int cheight =
(height + table.getRowMargin()) * table.getRowCount() + table.getRowHeight() + table.getRowMargin();
tableContainer.setPreferredSize(new Dimension(150, cheight));
tableContainer.add(table.getTableHeader(), BorderLayout.PAGE_START);
tableContainer.add(table, BorderLayout.CENTER);
tableScroll.setViewportView(tableContainer);
}
@Override
public void tableChanged(TableModelEvent e) {
// DO NOTHING
}
@SuppressWarnings("serial")
public static class TagRenderer extends DefaultTableCellRenderer {
public TagRenderer() {
setFont(FontTools.getFont11()); // Default size
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
Component val = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
AcquireMetadataTableModel model = (AcquireMetadataTableModel) table.getModel();
if (model.isCellEditable(row, column)) {
Color c = model.isValueRequired(row) && isEmptyValue(value) ? Color.RED : getForeground();
setBorder(BorderFactory.createDashedBorder(c, 2, 2));
}
Object tag = model.getValueAt(row, 0);
if (tag instanceof TagW) {
setValue(((TagW) tag).getFormattedTagValue(value, null));
}
return val;
}
private boolean isEmptyValue(Object value) {
if (value == null) {
return true;
}
if (value instanceof String) {
return !StringUtil.hasText((String) value);
}
return false;
}
}
@SuppressWarnings("serial")
public static class AcquireImageCellEditor extends AbstractCellEditor implements TableCellEditor {
// TODO more anatomy: http://dicom.nema.org/medical/dicom/2016c/output/chtml/part03/sect_10.5.html
private static final JComboBox<String> bodyPartsCombo =
new JComboBox<>(getBodyPartValues("weasis.acquire.meta.body.part")); //$NON-NLS-1$
private static final JComboBox<TagD.Sex> sexCombo = new JComboBox<>(TagD.Sex.values());
private static final JComboBox<Modality> modalityCombo =
new JComboBox<>(Modality.getAllModalitiesExceptDefault());
private static final JComboBox<String> studyDescCombo =
new JComboBox<>(getValues("weasis.acquire.meta.study.description", null)); //$NON-NLS-1$
private static final JComboBox<String> seriesDescCombo =
new JComboBox<>(getValues("weasis.acquire.meta.series.description", null)); //$NON-NLS-1$
static {
initCombo(bodyPartsCombo);
initCombo(sexCombo);
initCombo(modalityCombo);
initCombo(studyDescCombo);
initCombo(seriesDescCombo);
}
private Optional<TableCellEditor> editor;
@Override
public Object getCellEditorValue() {
return editor.map(e -> convertValue(e.getCellEditorValue())).orElse(null);
}
private Object convertValue(Object val) {
if (val instanceof TagD.Sex) {
return ((TagD.Sex) val).getValue();
} else if (val instanceof Modality) {
return ((Modality) val).name();
}
return val;
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
int column) {
TableCellEditor cellEditor;
Object tag = table.getModel().getValueAt(row, 0);
int tagID = 0;
boolean date = false;
boolean time = false;
if (tag instanceof TagW) {
tagID = ((TagW) tag).getId();
TagType type = ((TagW) tag).getType();
date = TagType.DICOM_DATE == type || TagType.DATE == type;
time = TagType.DICOM_TIME == type || TagType.TIME == type;
}
if (tagID == Tag.BodyPartExamined) {
cellEditor = new DefaultCellEditor(bodyPartsCombo);
} else if (tagID == Tag.PatientSex) {
cellEditor = new DefaultCellEditor(sexCombo);
} else if (tagID == Tag.Modality) {
cellEditor = new DefaultCellEditor(modalityCombo);
} else if (tagID == Tag.StudyDescription) {
cellEditor = getCellEditor(studyDescCombo);
} else if (tagID == Tag.SeriesDescription) {
cellEditor = getCellEditor(seriesDescCombo);
} else if (date) {
DateTableEditor teditor = buildDatePicker();
JMVUtils.setPreferredHeight(teditor.getDatePicker().getComponentToggleCalendarButton(),
table.getRowHeight(row));
cellEditor = teditor;
} else if (time) {
TimeTableEditor teditor = new TimeTableEditor(false, true, true);
teditor.getTimePickerSettings().fontInvalidTime = FontTools.getFont11(); // Default size
teditor.getTimePickerSettings().fontValidTime = FontTools.getFont11(); // Default size
teditor.getTimePickerSettings().fontVetoedTime = FontTools.getFont11(); // Default size
JMVUtils.setPreferredHeight(teditor.getTimePicker().getComponentToggleTimeMenuButton(),
table.getRowHeight(row));
cellEditor = teditor;
} else {
cellEditor = new DefaultCellEditor(new JTextField());
}
editor = Optional.of(cellEditor);
Component c = cellEditor.getTableCellEditorComponent(table, value, isSelected, row, column);
c.setFont(FontTools.getFont11()); // Default size
return c;
}
private static DefaultCellEditor getCellEditor(JComboBox<?> combo) {
if (combo.getItemCount() == 0) {
return new DefaultCellEditor(new JTextField());
} else {
return new DefaultCellEditor(combo);
}
}
private static void initCombo(JComboBox<?> combo) {
// Set before tooltip, otherwise update UI => remove selection listener
combo.setFont(FontTools.getFont11());
combo.setMaximumRowCount(15);
JMVUtils.setPreferredWidth(combo, 80);
// Update UI before adding the Tooltip feature in the combobox list
combo.updateUI();
JMVUtils.addTooltipToComboList(combo);
}
private DateTableEditor buildDatePicker() {
DateTableEditor d = new DateTableEditor(false, true, true);
DatePickerSettings settings = d.getDatePickerSettings();
settings.setFontInvalidDate(FontTools.getFont11());
settings.setFontValidDate(FontTools.getFont11());
settings.setFontVetoedDate(FontTools.getFont11());
Color btnBack = d.getDatePicker().getBackground();
JTextField tfSearch = new JTextField();
settings.setColor(DateArea.BackgroundOverallCalendarPanel, tfSearch .getBackground());
settings.setColor(DateArea.BackgroundMonthAndYearNavigationButtons, btnBack);
settings.setColor(DateArea.CalendarBackgroundNormalDates, btnBack);
// settings.setColor(DateArea.CalendarDefaultBackgroundHighlightedDates, tfSearch.getForeground());
// settings.setColor(DateArea.CalendarDefaultTextHighlightedDates, Color.ORANGE);
// settings.setColor(DateArea.CalendarBackgroundVetoedDates, Color.MAGENTA);
settings.setColor(DateArea.BackgroundClearLabel, btnBack);
settings.setColor(DateArea.BackgroundMonthAndYearNavigationButtons, btnBack);
settings.setColor(DateArea.BackgroundTodayLabel, btnBack);
settings.setColor(DateArea.BackgroundTopLeftLabelAboveWeekNumbers, btnBack);
settings.setColor(DateArea.BackgroundMonthAndYearMenuLabels, btnBack);
settings.setColor(DateArea.CalendarTextNormalDates, tfSearch.getForeground());
settings.setColor(DateArea.CalendarTextWeekdays, tfSearch.getForeground());
settings.setColor(DateArea.CalendarTextWeekNumbers, tfSearch.getForeground());
settings.setFormatForDatesCommonEra(LocalUtil.getDateFormatter());
settings.setFormatForDatesBeforeCommonEra(LocalUtil.getDateFormatter());
return d;
}
public static String[] getBodyPartValues(String property) {
String values = BundleTools.SYSTEM_PREFERENCES.getProperty(property, null);
if (values == null || !StringUtil.hasText(values)) {
return new String[] { "ABDOMEN", "ABDOMENPELVIS", "ADRENAL", "ANKLE", "AORTA", "ARM", "AXILLA", "BACK", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
"BLADDER", "BRAIN", "BREAST", "BRONCHUS", "BUTTOCK", "CALCANEUS", "CALF", "CAROTID", "CEREBELLUM", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"CERVIX", "CHEEK", "CHEST", "CHESTABDOMEN", "CHESTABDPELVIS", "CIRCLEOFWILLIS", "CLAVICLE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
"COCCYX", "COLON", "CORNEA", "CORONARYARTERY", "CSPINE", "CTSPINE", "DUODENUM", "EAR", "ELBOW", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"ESOPHAGUS", "EXTREMITY", "EYE", "EYELID", "FACE", "FEMUR", "FINGER", "FOOT", "GALLBLADDER", "HAND", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
"HEAD", "HEADNECK", "HEART", "HIP", "HUMERUS", "IAC", "ILEUM", "ILIUM", "JAW", "JEJUNUM", "KIDNEY", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"KNEE", "LARYNX", "LEG", "LIVER", "LSPINE", "LSSPINE", "LUNG", "MAXILLA", "MEDIASTINUM", "MOUTH", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
"NECK", "NECKCHEST", "NECKCHESTABDOMEN", "NECKCHESTABDPELV", "NOSE", "ORBIT", "OVARY", "PANCREAS", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
"PAROTID", "PATELLA", "PELVIS", "PENIS", "PHARYNX", "PROSTATE", "RADIUS", "RADIUSULNA", "RECTUM", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"RIB", "SCALP", "SCAPULA", "SCLERA", "SCROTUM", "SHOULDER", "SKULL", "SPINE", "SPLEEN", "SSPINE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
"STERNUM", "STOMACH", "SUBMANDIBULAR", "TESTIS", "THIGH", "THUMB", "THYMUS", "THYROID", "TIBIA", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"TIBIAFIBULA", "TLSPINE", "TMJ", "TOE", "TONGUE", "TRACHEA", "TSPINE", "ULNA", "URETER", "URETHRA", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
"UTERUS", "VAGINA", "VULVA", "WHOLEBODY", "WRIST", "ZYGOMA" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
}
String[] val = values.split(","); //$NON-NLS-1$
List<String> list = new ArrayList<>(val.length);
for (String s : val) {
String v = s.trim();
// VR must be CS
if (StringUtil.hasText(v) && v.length() <= 16) {
list.add(v);
}
}
return list.toArray(new String[list.size()]);
}
public static String[] getValues(String property, String defaultValues) {
String values = BundleTools.SYSTEM_PREFERENCES.getProperty(property, defaultValues);
if (values == null) {
return new String[0];
}
String[] val = values.split(","); //$NON-NLS-1$
List<String> list = new ArrayList<>(val.length);
for (String s : val) {
if (StringUtil.hasText(s)) {
list.add(s.trim());
}
}
return list.toArray(new String[list.size()]);
}
}
}